<?

class FieldSet {

	private $_fields = array ();
	private $_options = array ();
	private $_valid = true;
	private $_prefix = null;
	public  $_errors = array ();
	private $_relations = array ();
	private $_indexes = array ();
	private $_db_conn;
	private $_fieldset;
	public  $_access;
	public  $_field_access;
	private $_restrict;
	private $_verify_access;

	public function FieldSet ($options) {

		/* $this->_db_conn = $options["db_conn"];
		$this->_relation = $options["relation"];
		$this->_fieldset = $options["fieldset"]; */
		
		global $_APP_SESSION_USER;

		$this->_user_id = $_APP_SESSION_USER->id;

		$this->_db_conn = isset ($options["db_conn"]) ?
				$options["db_conn"] : null;
		$this->_relation = isset ($options["relation"]) ?
				$options["relation"] : null;
		$this->_relation_redirect = isset ($options["relation_redirect"]) ?
				$options["relation_redirect"] : null;
		$this->_fieldset = isset ($options["fieldset"]) ?
				$options["fieldset"] : null;
		$this->_prefix = isset ($options["prefix"]) ?
				$options["prefix"] : null;
		$this->_method = isset ($options["method"]) ?
				$options["method"] : null;
		$this->_access = isset ($options["access"]) ?
				$options["access"] : null;
		$this->_field_access = isset ($options["field_access"]) ?
				$options["field_access"] : null;
		$this->_verify_access = isset ($options["_verify_access"]) ?
				$options["_verify_access"] :
				"_app_verify_object_data_right";
				
		if (isset ($this->_access) && function_exists
				($va = $this->_verify_access) &&
				!$va ($this->_access))
				
			$this->_restrict = true;

		foreach ($options as $key => $val)

			$this->set_option ($key, $val);
	}

	public function parse ($load_option="view") {

		if ($this->_restrict) return;
		
		foreach ($this->_fieldset as $field) {

			if ($this->get_field ($field["name"]) && $load_option) {
				
				if (isset ($field["access"]) &&
						function_exists ($va = $this->_verify_access)) {
				
					if ($va ($field["access"])) {
				
						$this->get_field ($field["name"])->set_variable ("no_access_flag", false);
						$field["no_access_flag"] = false;
					}
				}
				
				continue;
			}

			if ((isset ($field["access"]) || isset ($this->_field_access)) &&
					function_exists ($va = $this->_verify_access)) {
				
				$data = null;

				if (isset ($field["access"])) {

					foreach ($field["access"]["data"] as $key => $val) {

						if (preg_grep ("/^{$load_option}$/", $val)) {
					
							$data = $key;
							reset ($field["access"]["data"]);
							break;
						}

					}
				}

				if (!$data && $this->_field_access) {

					foreach ($this->_field_access as $key => $val) {

						if (preg_grep ("/^{$load_option}$/", $val)) {
					
							$data = $key;
							reset ($this->_field_access);
							break;
						}

					}
				}

				/* $data=($data ? $data : isset ($this->_access["data"]) ?
							$this->_access["data"] : null); */
				$module=(isset ($field["access"]["module"]) ?
							$field["access"]["module"] :
							isset ($this->_access["module"]) ?
							$this->_access["module"] :
							null);
				$right=(isset ($field["access"]["right"]) ?
							$field["access"]["right"] :
							isset ($this->_access["right"]) ?
							$this->_access["right"] :
							null);

				$options = array (
					"module"=>$module,
					"right"=>$right,
					"data"=>$data,
				);

				//print_r ($options);

				$field["access"] = $options;

				if (!$va ($options)) {
				
					$field["no_access_flag"] = true;
				}
			}
				
			if (isset ($field["relation"]))
				$this->set_relation ($field);
			if (isset ($field["index_key"]))
				$this->set_index ($field);
			if (isset ($field["primary_key"])) $this->set_option
					("primary_key", $field["name"]);
			if (isset ($field["relation_key"])) $this->set_option
					("relation_key", $field["name"]);
			array_push ($this->_fields, new Field ($field, $this->_prefix));
			if (isset ($field["relation"])) {
			
				$field_rkey = $field;
				$field_rkey["rkey"] = true;
				$field_rkey["name"] = $field_rkey["name"] . "_rkey";
				array_push ($this->_fields, new Field ($field_rkey, $this->_prefix));
			}
			//reset ($this->_fields);
		}
	}
	
	public function get_access ($option) {
		
		return $this->_access[$option];
	}

	private function set_relation ($field) {

		array_push ($this->_relations, $field);
	}

	private function set_index ($field) {

		array_push ($this->_indexes, $field);
	}

	function add_field ($field, $location=null) {

		if ($location) {

			array_splice ($this->_fields, $location, 0, array (new Field ($field, $this->_prefix)));
		} else array_push ($this->_fields, new Field ($field, $this->_prefix));
	}

	function next_field () {

		$field = current ($this->_fields);
		next ($this->_fields);
		#if (!$field) reset ($this->_fields);
		return $field;
	}

	function validate () {

		if ($this->_restrict) return false;
		
		$this->set_option ("valid", true);

		reset ($this->_fields);

		foreach ($this->_fields as $field) {

			if (!$field->get_variable ("value") &&
					$field->get_variable ("disabled") ||
					($field->get_variable ("optional") ||
					$field->get_variable ("no_access_flag") ||
					($field->get_variable ("primary_key")
					||$field->get_variable ("relation_key"))))

				continue;
			if ($field->get_variable ("rkey")) continue;

			if (!$field->validate ()) {

				$this->set_option ("valid", false);
				$this->set_error (
					$field->get_option ("error_msg"));
			}
		}

		reset ($this->_fields);

		return ($this->get_option ("valid"));
	}

	function set_error ($error_msg) {

		array_push ($this->_errors, $error_msg);
	}

	function set_field ($field, $id=null) {

		array_splice ($this->_fields, $id, 0, $field);
	}

	function get_field ($field_name) {

		reset ($this->_fields);

		foreach ($this->_fields as $field) {

			if ($field->get_variable ("name") == $field_name) {

				reset ($this->_fields);
				return $field;
			}
		}

		reset ($this->_fields);

		return null;
	}

	function drop_field ($field_name) {

		$i = 0;

		reset ($this->_fields);

		foreach ($this->_fields as $field) {

			if ($field->get_variable ("name") == $field_name) {

				reset ($this->_fields);
				return  array_splice ($this->_fields, $i, 1);
			}

			$i++;
		}

		reset ($this->_fields);
	}

	function store () {

		if ($this->_restrict) return false;
		
		$primary_key = null;
		$insert_id = null;
		$statement = null;
		$field = null;
		$sql_fields = null;
		$sql_values = null;
		$fields = array ();
		$values = array ();
		$multiple = array ();

		reset ($this->_fields);

		while ($field = $this->next_field ()) {

			/* if (!($field->get_variable ("value")) &&
					$field->get_variable ("optional"))

				continue; */
			if ($field->get_variable ("no_access_flag")) continue;
			if ($field->get_variable ("multiple")) {

				if ($field->get_variable ("rkey")) continue;
				array_push ($multiple, $field);
				continue;
			}
			if ($field->get_variable ("internal")) continue;
			if ($field->get_variable ("excluded")) continue;
			if ($field->get_variable ("rkey")) continue;

			if ($field->get_variable ("primary_key")) {

				$primary_key = $field->get_variable ("name");
				continue;
			}

			// if (!$field->get_variable ("value")) continue;

			array_push ($fields, $field->get_variable ("name"));
			array_push ($values, preg_replace ("/'/", "''", $field->get_variable ("value")));
		}

		reset ($this->_fields);

		$sql_fields = join (",", $fields);
		$sql_values = "'" . join ("','", $values) . "'";

		$this->set_option ("insert_sql", $statement=<<<SQL
INSERT INTO {$this->get_option ("relation")} ($sql_fields)
VALUES ($sql_values)
SQL
		);

		#print $statement . "<br>";

		if (!$this->_db_conn->query ($statement)) {

			$this->set_option ("sql_query_error",
					"DB query failure: " . mysql_error());

			return false;
		}

		if (!$this->get_option ("relation_key"))

			$this->get_field ($this->get_option ("primary_key"))
				->set_variable ("value", $insert_id);

		$this->set_option ("insert_id", $insert_id =
				$this->_db_conn->insert_id ());

		if ($this->get_option ("primary_key")) 

			$this->get_field ($primary_key)->set_variable ("value",
					$_REQUEST[$this->_prefix . $primary_key] = $insert_id);

		foreach ($multiple as $field) {

			$callback = $field->get_variable ("multiple_callback");

			if (function_exists ($callback))

				$callback($this, $field, true);
		}

		return true;
	}

	function update () {

		if ($this->_restrict) return false;
		
		$primary_key = null;
		$primary_val = null;
		$statement = null;
		$field = null;
		$sql_fields = null;
		$fields = array ();
		$multiple = array ();

		reset ($this->_fields);

		while ($field = $this->next_field ()) {

			/* if (!($field->get_variable ("value")) &&
					$field->get_variable ("optional"))

				continue; */
			if ($field->get_variable ("multiple")) {

				if ($field->get_variable ("rkey")) continue;
				array_push ($multiple, $field);
				continue;
			}
			if ($field->get_variable ("internal")) continue;
			if ($field->get_variable ("excluded")) continue;
			if ($field->get_variable ("rkey")) continue;
			if ($field->get_variable ("no_access_flag")) continue;

			if ($field->get_variable ("primary_key")) {

				$primary_key = $field->get_variable ("name");
				$primary_val = $field->get_variable ("value");
				continue;
			} elseif ($field->get_variable ("relation_key")) {

				$primary_key = $field->get_variable ("name");
				$primary_val = $field->get_variable ("value");
				continue;
			} elseif ($field->get_variable ("index_key")) {

				$primary_key = $field->get_variable ("name");
				$primary_val = $field->get_variable ("value");
				continue;
			}

			// if (!$field->get_variable ("value")) continue;

			array_push ($fields, $field->get_variable ("name") .
					"='" .  preg_replace ("/'/","''",$field->get_variable ("value")) .  "'");
		}

		reset ($this->_fields);
		
		$_match = $primary_key ? array("{$primary_key} = {$primary_val}") : array ();
		if (count ($this->_indexes)) {
			foreach ($this->_indexes as $field) {
				array_push ($_match, "{$field["name"]}='{$field["value"]}'");
			}
		}
		
		$match = join (" AND ", $_match);
		$sql_fields = join (",", $fields);

		$this->set_option ("update_sql", $statement=<<<SQL
UPDATE {$this->get_option ("relation")} SET {$sql_fields}
WHERE {$match}
SQL
		);

		#print $statement;

		if (!$this->_db_conn->query ($statement)) {

			$this->set_option ("sql_query_error",
					"DB query failure: " . mysql_error());

			return false;
		}

		foreach ($multiple as $field) {

			$callback = $field->get_variable ("multiple_callback");

			if (!$field->get_variable("no_access_flag") && function_exists ($callback))

				$callback($this, $field, true);
		}

		return true;
	}

	function retrieve ($get_relations=true) {

		if ($this->_restrict) return false;
		
		$sql_object = null;
		$sql_query = null;
		$statement = null;
		$primary_key = $this->get_option ("primary_key") ?
				$this->get_option ("primary_key") :
				$this->get_option ("relation_key");
		$primary_val = ($primary_val = $this->get_field ($primary_key)) ?
				$primary_val->get_variable ("value") : null;
		$_relations = array ($this->get_option ("relation"));
		$rel = $this->get_option ("relation");
		$_fields = array ($rel . ".*");
		$_match = $primary_key ? array ("{$rel}.{$primary_key}='{$primary_val}'") : array ();
		$multiple = array ();

		reset ($this->_relations);

		if ($get_relations) {

			foreach ($this->_relations as $field) {

				if ($field["internal"]) continue;
				$relation = $field["relation"];
				array_push ($_match, $rel . "." .
						$field["name"] . "=" .
						$relation["foreign"] . "." .
						$relation["key"]);
				array_push ($_fields, $relation["foreign"] .
						"." . $relation["field"] .
						" AS " . $field["name"]);
				array_push ($_fields, $rel . "." . $field["name"] .
						" AS " . $field["name"] . "_rkey");
				array_push ($_relations, $relation["foreign"]);
			}
		}
		
		if (count ($this->_indexes)) {
			foreach ($this->_indexes as $field) {
				array_push ($_match, "{$rel}.{$field["name"]}='{$field["value"]}'");
			}
		}

		reset ($this->_relations);

		$fields = join (",", $_fields);
		$relations = join (",", $_relations);
		$match = join (" AND ", $_match);
		$this->set_option ("select_sql", $statement=<<<SQL
SELECT {$fields} FROM {$relations}
WHERE {$match}
SQL
		);

		#print $statement . "<br>";

		if (!($sql_query = $this->_db_conn->query ($statement))) {

			$this->set_option ("sql_query_error",
					"DB query failure: " . mysql_error());

			return false;
		}

		$sql_object = $this->_db_conn->fetch_object ($sql_query);

		reset ($this->_fields);

		while ($field = $this->next_field ()) {

			$field_name = $field->get_variable ("name");
			$field_value = $sql_object->$field_name;
			$field->set_variable ("value",
					$_REQUEST[$this->_prefix . $field_name] = $field_value);
		}

		reset ($this->_fields);

		while ($field = $this->next_field ()) {

			if ($field->get_variable ("multiple"))

				array_push ($multiple, $field);
		}

		reset ($this->_fields);

		foreach ($multiple as $field) {

			$callback = $field->get_variable ("multiple_callback");

			if (function_exists ($callback))

				$field->set_variable ("value",
					$callback($this, $field, false,
					($get_relations ? false : true)));
		}

		reset ($this->_fields);

		return true;
	}

	function trash_bin ($relation,$key,$id) {

		
		if (!$rdata = $this->_db_conn->query ("SELECT * FROM {$relation} WHERE {$key}='{$id}'")) {

			$this->set_option ("sql_query_error",
					"DB query failure: " . mysql_error());
			return false;
		}

		$data = mysql_escape_string (serialize ($this->_db_conn->fetch_array ($rdata, MYSQL_ASSOC)));

		if (!$this->_db_conn->query ("INSERT INTO trash_bin(item,data,user_id) VALUES ('{$relation}','{$data}','{$this->_user_id}')")) {
			$this->set_option ("sql_query_error", "DB query failure: " . mysql_error());
			return false;
		}

		return true;
	}

	function remove () {

		if ($this->_restrict) return false;
		
		$primary_key = ($relation_key =
				$this->get_option("relation_redirect_key")) ?
				$relation_key :
				$this->get_option ("primary_key") ?
				$this->get_option ("primary_key") :
				$this->get_option ("relation_key");
		$primary_val = ($primary_val = $this->get_field ($primary_key)) ?
				$primary_val->get_variable ("value") : null;

		$_match = $primary_key ? array("{$primary_key} = {$primary_val}") : array ();

		if (count ($this->_indexes)) {

			foreach ($this->_indexes as $field) {
				array_push ($_match, "{$field["name"]}='{$field["value"]}'");
			}
		}
		
		if (!$this->trash_bin ($this->_relation, $primary_key, $primary_val)) {
		
			$this->set_option ("fieldset_trash_bin_error",
					"Could not store the item into the trash bin.");
			return false;
		}

		$match = join (" AND ", $_match);
		$relation = $this->get_option ("relation_redirect") ? $this->get_option ("relation_redirect") : $this->get_option ("relation");

		$this->set_option ("delete_sql", $statement=<<<SQL
DELETE FROM {$relation} WHERE {$match}
SQL
		);

		#print $statement . "<br>";

		if (!$this->_db_conn->query ($statement)) {

			$this->set_option ("sql_query_error",
					"DB query failure: " . mysql_error());

			return false;
		}

		return true;
	}

	function set_option ($option, $value) {

		$this->_options[$option] = $value;
	}

	function get_option ($option) {

		return $this->_options[$option];
	}
}

class Field {

	var $_options = array ();
	var $_variables = array ();
	var $_prefix = null;

	function Field ($fields, $prefix = null) {

		$this->_prefix = $prefix;
		$this->set_option ("valid", true);

		foreach ($fields as $key => $val)
				$this->set_variable ($key, $val);

		/* if (!$this->get_variable ("value")) {

			$this->set_variable ("value", $_REQUEST[$this->_prefix . $this->get_variable ("name")]);
		}*/

		$this->set_variable ("value", isset ($_REQUEST[$this->_prefix . $fields["name"]]) ?
				$_REQUEST[$this->_prefix . $fields["name"]] :
				$this->get_variable ("value"));
	}

	function set_option ($option, $value) {

		$this->_options[$option] = $value;
	}

	function get_option ($option) {

		return $this->_options[$option];
	}

	function set_variable ($variable, $value) {

		$this->_variables[$variable] = $value;
	}

	function get_variable ($variable) {

		return $this->_variables[$variable];
	}

	function set_error ($msgstr = null) {

		if ($this->get_option ("value") &&
				$this->get_option ("optional")) return;
		$field = $this->get_variable ("label") ?
				$this->get_variable ("label") :
				$this->get_variable ("name");
		$msgstr = $this->get_variable ("errmsg");

		if ($msgstr) $msg = sprintf ($msgstr, $field,
				$this->get_variable ("type"));
		else $msg = $field . ": must be a valid " .
				$this->get_variable ("type") . " type." ;

		$this->set_option ("error_msg", $msg);
		$this->set_option ("valid", false);
	}

	function get_error () {

		return $this->get_option ("error_msg");
	}

	function validate ($value=null) {

		if (!$value) $value = $this->get_variable ("value");

		if (is_array ($value)) {

			foreach ($value as $option) {

				$this->validate ($option);
			}

			return $this->get_option ("valid");
		}

		switch ($this->get_variable ("type")) {

			case "character":
				if (!preg_match ("/^[_a-zA-Z \-\:\&\.]+$/",
						$value)) $this->set_error ();
				break;
			case "alphanumeric":
				if (!preg_match ("/^[_a-zA-Z][a-zA-Z_ \-0-9:\.,']+$/",
						$value)) $this->set_error ();
				break;
			case "address":
				if (!preg_match ("/^[a-zA-Z_#\&\. \-0-9]+$/",
						$value)) $this->set_error ();
				break;
			case "numeric":
				if (!preg_match ("/^[0-9]+$/",
						$value)) $this->set_error ();
				break;
			case "serial":
				if (!preg_match ("/^[a-zA-Z0-9_]+[a-zA-Z0-9\_\-]*$/",
						$value)) $this->set_error ();
				break;
			case "credicard":
				if (!preg_match ("/(\d){16,}/",
						$value)) $this->set_error ();
				break;
			case "phone":
				if (!preg_match ("/^(\d)?\ ?\.?\-?\(?((\d){3,3})?\)? ?\.?\-?(\d)\d* ?\.?\-?\d*$/",
						$value)) $this->set_error ();
				break;
			case "email":
				if (!preg_match ("/^[^_]\w*(\.\w*)?\@[^_]\w*(\.\w*)+/",
						$value)) $this->set_error ();
				break;
			case "domain":
			case "url":
				if (!preg_match ("/^(http[s]://)/i",
						$value)) $this->set_error ();
				break;
			case "currency":
			case "decimal":
				if (preg_match ("/\.\d$/",
						$value)) $value .= "0";
				if ($value && (!preg_match ("/\./", $value)
						|| !preg_match ("/\.\d{2,7}$/",
						$value)))
					$value .= ".00";
				if (!preg_match ("/^\\$?\s*((\d{1,3},)?(\d{3,3},)*)?\d*\.\d{2,7}$/",
						$value)) $this->set_error ();
				$this->set_variable ("value", $value);
				break;
			case "any":

				#if (!preg_match ("//",
				#		$value)) $this->set_error ();
				break;
		}

		return $this->get_option ("valid");
	}
}

?>
